const PENDING = 'pending'   // 等待
const FULFILLED = 'fulfilled'   // 成功
const REJECTED = 'rejected'     // 失败

class MyPromise {
    constructor (executor) {
        // 传入一个执行器并立即执行（执行器两个参数是函数）
        executor(this.resolve, this.reject)
    }
    
    // promise 状态初始值
    status = PENDING
    // promise 成功 resolve 传递的值（默认值）
    value = undefined
    // promise 失败 reject 传递的原因（默认值）
    reason = undefined
    
    // 成功回调
    // successCallback = undefined
    // 因为多次调用 then 方法，所以用数组来存储多个回调函数
    successCallback = []
    // 失败回调
    // failCallback = undefined
    failCallback = []
    
    resolve = value => {
        // 如果状态不是等待，阻止程序向下执行
        // 因为 Promise 一旦状态改变，是不可以再改变的 
        if(this.status !== PENDING) return
        // 将状态更改为成功
        this.status = FULFILLED
        // 保存 promise 成功传过来的值
        this.value = value 
        
        // 判断成功回调是否存在，如果存在，则调用
        // this.successCallback && this.successCallback(this.value)
        // 因为多次调用 then 方法，回调函数存储在数组中，所以需要循环依次调用（队列先进先出）
        while(this.successCallback.length) this.successCallback.shift()(this.value)
    }
    
    reject = reason => {
        // 如果状态不是等待，阻止程序向下执行
        // 因为 Promise 一旦状态改变，是不可以再改变的 
        if(this.status !== PENDING) return
        // 将状态更改为失败
        this.status = REJECTED
        // 保存 promise 失败传过来的原因
        this.reason = reason 
        
        // 判断失败回调是否存在，如果存在，则调用
        // this.failCallback && this.failCallback(this.reason)
        // 因为多次调用 then 方法，回调函数存储在数组中，所以需要循环依次调用（队列先进先出）
        while(this.failCallback.length) this.failCallback.shift()(this.reason)
    }
    
    // 类的所有方法都是定义在类的 prototype 属性上面的
    // 即类的方法是被定义在原型对象中的，此处 then 方法就是
    // then 方法期望的参数是回调函数
    then (successCallback, failCallback) {
    
        // 创建新的 Promise 对象作为返回值，实现链式调用 then 方法
        let newPromise = new MyPromise((resolve, reject) => {
        
            // 判断状态
            if (this.status === FULFILLED) {
                
                // 异步任务，保证获取到 newPromise 对象
                setTimeout(() => {
                    // 新的 Promise 对象的执行器调用 resolve 方法，参数是上一个 then 方法的回调函数返回值
                    // 因为是实现 then 方法的链式调用，所以只在 fulfilled 状态处理即可
                    let x = successCallback(this.value)
                    // 注释掉 resolve(x)，因为返回值有两种
                    // 判断 x 的值是普通值还是 Promise 对象
                    // 如果是普通值，直接调用 resolve
                    // 如果是 Promise 对象，查看 Promise 对象返回的结果
                    // 再根据 Promise 对象返回的结果，决定调用 resolve 还是 reject
                    // 增加判断 newPromise 跟 x 是否是同一个 Promise 对象，避免程序异常
                    resolvePromise(newPromise, x, resolve, reject)
                }, 0)
                
            } else if (this.status === REJECTED) {
                failCallback(this.reason)
            } else {
            
                // pending 等待状态
                // 如果 Promsie 实例对象传入的执行器存在异步任务时
                // 会开启异步任务后立即执行 then，此时状态还是处于 pending
                // 将成功回调和失败回调函数通过类的属性储存起来
                // 以便于异步任务结束后在 resolve 或 reject 中执行 then 的回调函数
                
                // 多次调用 then 方法，回调函数存储到数组中
                // this.successCallback = successCallback
                this.successCallback.push(successCallback)
                // this.failCallback = failCallback
                this.failCallback.push(failCallback)
                
            }
            
        })
        
        return newPromise
        
    }
}

function resolvePromise (newPromise, x, resolve, reject) {
    // 判断 newPromise 与 x 是否是同一个 Promise 对象
    if (newPromise === x) {
        // 触发失败回调函数
        reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        return
    }
    // 判断 x 是否是 Promise 实例
    if (x instanceof MyPromise) {
        // Promise 对象
        // x.then(value => resolve(value), reason => reject(reason))
        // 因为 resolve 跟 reject 都是函数，且在这里是作为 then 方法的回调函数，所以可以简化为
        x.then(resolve, reject)
    } else {
        // 普通值
        resolve(x)
    }
}